﻿using System;
using System.Collections;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.ComponentModel;
using System.Threading;
using Common.Protocol;
using LogicAndDB;
namespace WebHost
{
    /// <summary>
    /// assume that there is no further control request before the current one
    ///is responsed.
    ///TODO:登录代码有待实现，目前是只要上来就登录成功
    /// </summary>
    public class CGlobalContext
    {
        public const string APP_DATA_KEY = "APP_DATA_KEY";
        public const string SESSION_DATA_KEY = "SESSION_DATA_KEY";
        public const string SESSION_TYPE_KEY = "SESSION_TYPE_KEY";
        public const string REQUEST_CONTENT = "REQUEST_CONTENT";
        //public const string REQUEST_CONTENT = "REQUEST_CONTENT";

        public const string SESSION_SOURCE_CONTROLLER = "SESSION_SOURCE_CONTROLLER";
        public const string SESSION_SOURCE_PPT = "SESSION_SOURCE_PPT";

        Object _mutex;
       
        public Hashtable ControlSessions
        {
            get;set;
        }

        public CGlobalContext()
        {
            _mutex = new Object();
            ControlSessions = new Hashtable();
            DBContainer.Init();
        }

        public String Login(string data)
        {
            LoginReq cmdReq = Command.FromJson<LoginReq>(data);
            LoginResp resp = new LoginResp();
            int uid = 0;

            //Authenticate

            ControlSession controlSession = null;
            if (cmdReq.SourceType == LoginReq.SOURCETYPE_MOBILE)
            {
                controlSession = LoginAsController(uid);
                HttpContext.Current.Session[SESSION_TYPE_KEY] = SESSION_SOURCE_CONTROLLER;
            }
            else if (cmdReq.SourceType == LoginReq.SOURCETYPE_PPT)
            {
                controlSession = LoginAsReceiver(uid);
                HttpContext.Current.Session[SESSION_TYPE_KEY] = SESSION_SOURCE_PPT;
            }

            HttpContext.Current.Session[SESSION_DATA_KEY] = controlSession;

            if (controlSession == null)
            {
                resp.AckCode = LoginResp.LOGINACK_FAIL;
                resp.DetailMessage = "System interior error.";
            }
            else
            {
                resp.AckCode = LoginResp.LOGINACK_SUCCESS;
            }
            return resp.ToJson();
        }

        protected ControlSession LoginAsController(int uid)
        {
            ControlSession controlSession = null;
            lock (_mutex)
            {
                if (ControlSessions.ContainsKey(uid))
                {
                    controlSession = (ControlSession)ControlSessions[uid];
                    controlSession.OnUserLogin(ControlSession.FROM_MOBILE);
                }
                else
                {
                    controlSession = ControlSession.Create(uid, ControlSession.FROM_MOBILE);
                    ControlSessions.Add(uid, controlSession);
                }
            }
            return controlSession;
        }

        protected ControlSession LoginAsReceiver(int uid)
        {
            ControlSession controlSession = null;
            lock (_mutex)
            {
                if (ControlSessions.ContainsKey(uid))
                {
                    Logger.Write("get session for:"+uid);
                    controlSession = (ControlSession)ControlSessions[uid];
                    controlSession.OnUserLogin(ControlSession.FROM_PC);
                }
                else
                {
                    Logger.Write("create session for:"+uid);
                    controlSession = ControlSession.Create(uid, ControlSession.FROM_PC);
                    ControlSessions.Add(uid, controlSession);
                }
            }
            return controlSession;
        }

        /// <summary>
        /// request/response called in different theads
        /// </summary>
        public String Request(String data)
        {
            ControlReq reqCmd = Command.FromJson<ControlReq>(data);
            ControlResp respCmd = new ControlResp();
            ControlSession controlSession = HttpContext.Current.Session[SESSION_DATA_KEY] as ControlSession;
            if (controlSession == null)
            {
                respCmd.AckCode = ControlResp.CONTROLACK_NOTLOGIN;
                Logger.Write("not logged in");
                return respCmd.ToJson();
            }

            lock (_mutex)
            {
                try
                {
                    controlSession.RequestContentData = data;
         
                }
                catch (Exception e)
                {
                    //异常只可能是未登录。其实应该改进一下
                    respCmd.AckCode = ControlResp.CONTROLACK_NOTLOGIN;
                    respCmd.DetailMessage = e.Message;
                    return respCmd.ToJson();
                }
            }

            controlSession.RequestStartEvent.Set();

            if (reqCmd.GetProcessType() != ControlReq.ProcessType.NoResoponse)
            {
                Logger.Write("wait for response");
                controlSession.RequestEndEvent.WaitOne(CONTROL_REQ_TIMEOUT);
            }

            lock (_mutex)
            {
                if (controlSession.PPTSideResponse != null && controlSession.PPTSideResponse.Dirty)
                {
                    return controlSession.ResponseContentData;
                }
                else
                {
                    respCmd.AckCode = ControlResp.CONTROLACK_NORESPONSE;
                    return respCmd.ToJson();
                }
            }
        }

        /// <summary>
        /// request/response called in different threads
        /// </summary>
        public void Response(String data)
        {
            ControlSession controlSession = HttpContext.Current.Session[SESSION_DATA_KEY] as ControlSession;
            if (controlSession == null)
            {
                Logger.Write("empty session when responsing");
                return;
            }

            lock (_mutex)
            {
                try
                {
                    controlSession.ResponseContentData = data;
                }
                catch (Exception e)
                {
                    return;
                }
            }

            controlSession.RequestEndEvent.Set();
        }

        const int CONTROL_REQ_TIMEOUT = 1000;//1S
        /// <summary>
        /// only be called when ppt side just connected, or
        /// it receive the _requestStartEvent;
        /// </summary>
        /// <returns></returns>
        public String CheckRequest(String data)
        {
            CheckResp respCmd = new CheckResp();
            ControlSession controlSession = HttpContext.Current.Session[SESSION_DATA_KEY] as ControlSession;
            if (controlSession == null)
            {
                Logger.Write("empty session,sessionid:"+HttpContext.Current.Session.SessionID);
                respCmd.AckCode = CheckResp.CHECKACK_NONE;
                return respCmd.ToJson();
            }

            lock (_mutex)
            {
                try
                {
                    if (controlSession.ControllerRequest != null && controlSession.ControllerRequest.Dirty)
                    {
                        ControlReq reqCmd = Command.FromJson<ControlReq>(controlSession.RequestContentData);
                        respCmd = reqCmd.toResp();
                        respCmd.AckCode = CheckResp.CHECKACK_SUCCESS;
                        if (respCmd.Operation == null)
                        {
                            Logger.Write("empty operation");
                        }
                        return respCmd.ToJson();
                    }
                }
                catch (Exception e)
                {
                    Logger.Write("failed to check control request");
                    respCmd.AckCode = CheckResp.CHECKACK_NONE;
                    return respCmd.ToJson();
                }
            }
            //WAIT UNTIL 10S BEFORE IIS REQUEST TIMEOUT.
            int milliSecondsToWait = HttpContext.Current.Session.Timeout * 60 * 1000 -10;
            Logger.Write("sleep:"+milliSecondsToWait);
            controlSession.RequestStartEvent.WaitOne(milliSecondsToWait);
            lock (_mutex)
            {
                try
                {
                    if (controlSession.ControllerRequest != null && controlSession.ControllerRequest.Dirty)
                    {
                        ControlReq reqCmd = Command.FromJson<ControlReq>(controlSession.RequestContentData);
                        respCmd = reqCmd.toResp();
                        respCmd.AckCode = CheckResp.CHECKACK_SUCCESS;
                        if (respCmd.Operation == null)
                        {
                            Logger.Write("empty operation");
                        }
                        return respCmd.ToJson();
                    }
                    else
                    {
                        Logger.Write("nothing to return for check request");
                        Logger.Write(controlSession.ControllerRequest.ToString());
                        respCmd.AckCode = CheckResp.CHECKACK_NONE;
                        return respCmd.ToJson();
                    }
                }
                catch (Exception e)
                {
                    respCmd.AckCode = CheckResp.CHECKACK_NONE;
                    return respCmd.ToJson();
                }
            }

        }

        public void OnSessionEnd(System.Web.SessionState.HttpSessionState Session)
        {
            if (Session[CGlobalContext.SESSION_DATA_KEY] != null)
            {

                ControlSession controlSession = Session[CGlobalContext.SESSION_DATA_KEY] as ControlSession;
                lock (_mutex)
                {
                    if (Session[CGlobalContext.SESSION_TYPE_KEY].ToString() == CGlobalContext.SESSION_SOURCE_CONTROLLER)
                        controlSession.ControllerRequest = null;
                    else if (Session[CGlobalContext.SESSION_TYPE_KEY].ToString() == CGlobalContext.SESSION_SOURCE_PPT)
                        controlSession.PPTSideResponse = null;
                    if (controlSession.ControllerRequest == null && controlSession.PPTSideResponse == null)
                    {
                        Logger.Write("remove session:"+controlSession.UID+" for session:"+HttpContext.Current.Session.SessionID);
                        ControlSessions.Remove(controlSession.UID);
                    }
                }
            }
        }
    }
}
